/**
 * www.easyplatform.cn ©2016
 */
package cn.easyplatform.studio.cmd.entity;

import cn.easyplatform.entities.beans.project.ProjectBean;
import cn.easyplatform.entities.beans.table.TableBean;
import cn.easyplatform.entities.beans.table.TableField;
import cn.easyplatform.entities.beans.table.TableFk;
import cn.easyplatform.entities.beans.table.TableIndex;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.studio.dao.BizDao;
import cn.easyplatform.studio.dao.DaoException;
import cn.easyplatform.studio.dao.Page;
import cn.easyplatform.studio.interceptor.Command;
import cn.easyplatform.studio.interceptor.CommandContext;
import cn.easyplatform.studio.vos.ScanVo;
import cn.easyplatform.studio.vos.TableVo;
import cn.easyplatform.type.EntityType;
import cn.easyplatform.type.FieldType;

import java.util.*;

/**
 * @author <a href="mailto:shiny_vc@163.com">陈云亮</a> <br/>
 * @since 2.0.0 <br/>
 */
public class ScanCmd implements Command<ScanVo> {

    private String dbId;

    private int pageSize;

    private int pageNo;

    private String searchValue;

    private boolean isInit;

    public ScanCmd(String dbId, int pageSize, int pageNo, String searchValue,
                   boolean isInit) {
        this.dbId = dbId;
        this.pageSize = pageSize;
        this.pageNo = pageNo;
        this.searchValue = searchValue;
        this.isInit = isInit;
    }

    @Override
    public ScanVo execute(CommandContext cc) {
        try {
            cc.getUser().setBusying(true);
            ProjectBean pb = cc.getProject();
            if (pb.getBizDb().equals(dbId))
                dbId = "";
            StringBuilder sb = new StringBuilder(
                    "SELECT entityId,name,desp,type,subType,content FROM ");
            sb.append(pb.getEntityTableName()).append(" WHERE type='")
                    .append(EntityType.TABLE.getName()).append("' AND ");
            if (Strings.isBlank(dbId))
                sb.append("(subType='' OR subType IS NULL OR subType='")
                        .append(pb.getBizDb()).append("')");
            else
                sb.append("subType='").append(dbId).append("'");
            if (pageSize > 0)
                return scanPage(cc, sb);
            return scanAll(cc, sb);
        } finally {
            cc.getUser().setLastAccessTime(new Date());
            cc.getUser().setBusying(false);
        }
    }

    private ScanVo scanAll(CommandContext cc, StringBuilder sb) {
        sb.append(" ORDER BY entityId");
        List<TableBean> data = cc.getEntityDao().selectEntity(sb.toString(),
                null);
        List<TableBean> tables = cc.getBizDao(dbId).getTables();
        List<TableVo> result = new ArrayList<TableVo>();
        List<TableVo> sourceNew = new ArrayList<TableVo>();
        List<TableVo> targetNew = new ArrayList<TableVo>();
        for (TableBean source : data) {
            TableVo tv = new TableVo(source.getId(), source.getName());
            tv.setSource(source);
            Iterator<TableBean> itr = tables.iterator();
            boolean exists = false;
            while (itr.hasNext()) {
                TableBean target = itr.next();
                target.setSubType(dbId);
                if (source.getId().equalsIgnoreCase(target.getId())) {// 有真实表对应
                    target.setView(source.getView());
                    processTable(tv, source, target);
                    exists = true;
                    itr.remove();
                    break;
                }// id equals
            }// itr
            if (!exists) {
                tv.setState(TableVo.NEW);
                sourceNew.add(tv);
            } else if (tv.getState() == TableVo.DIFF)
                result.add(tv);
        }
        for (TableBean target : tables) {
            TableVo dv = new TableVo(target.getId(), target.getName());
            dv.setTarget(target);
            dv.setState(TableVo.REVERT);
            targetNew.add(dv);
        }
        Collections.sort(result, new Comparator<TableVo>() {

            @Override
            public int compare(TableVo o1, TableVo o2) {
                return o2.getState() - o1.getState();
            }

        });
        data = null;
        tables = null;
        return new ScanVo(result, sourceNew, targetNew);
    }

    private ScanVo scanPage(CommandContext cc, StringBuilder sb) {
        String[] params = new String[0];
        if (!Strings.isBlank(searchValue)) {
            params = new String[2];
            params[0] = "%" + searchValue + "%";
            params[1] = params[0];
            sb.append(" AND (entityId LIKE ? OR name LIKE ?)");
        }
        Page page = new Page(pageSize);
        page.setGetTotal(isInit);
        page.setPageNo(pageNo);
        page.setOrderBy("entityId");
        List<TableBean> data = cc.getEntityDao().selectEntity(sb.toString(),
                page, params);
        BizDao dao = cc.getBizDao(dbId);
        List<TableVo> result = new ArrayList<TableVo>();
        List<TableVo> sourceNew = new ArrayList<TableVo>();
        for (TableBean source : data) {
            TableVo tv = new TableVo(source.getId(), source.getName());
            tv.setSource(source);
            TableBean target = null;
            try {
                target = dao.revertTable(tv.getId());
            } catch (DaoException e) {
            }
            if (target != null) {
                target.setSubType(dbId);
                target.setView(source.getView());
                processTable(tv, source, target);
                result.add(tv);
            } else {
                tv.setState(TableVo.NEW);
                sourceNew.add(tv);
            }
        }
        Collections.sort(result, new Comparator<TableVo>() {

            @Override
            public int compare(TableVo o1, TableVo o2) {
                return o2.getState() - o1.getState();
            }

        });
        data = null;
        return new ScanVo(result, sourceNew, isInit ? page.getTotalCount() : -1);
    }

    private void processTable(TableVo tv, TableBean source, TableBean target) {
        // 先检查栏位
        List<String> diff = new ArrayList<String>();
        List<String> sourceFieldNew = new ArrayList<String>();
        List<String> targetFieldNew = new ArrayList<String>();
        List<TableField> tmp = new ArrayList<TableField>(target.getFields());
        for (TableField sf : source.getFields()) {
            String name = sf.getName();
            TableField tf = null;
            Iterator<TableField> it = tmp.iterator();
            while (it.hasNext()) {
                TableField t = it.next();
                if (name.equalsIgnoreCase(t.getName())) {
                    tf = t;
                    it.remove();
                    break;
                }
            }
            if (tf == null)
                sourceFieldNew.add(name.toUpperCase());
            else if (!checkField(sf, tf))
                diff.add(name.toUpperCase());
        }
        if (!tmp.isEmpty()) {
            for (TableField tf : tmp)
                targetFieldNew.add(tf.getName().toUpperCase());
        }
        tmp = null;
        if (!diff.isEmpty()) {
            tv.setDiffFields(diff);
            tv.setState(TableVo.DIFF);
            tv.setDiffByField(true);
        }
        if (!sourceFieldNew.isEmpty()) {
            tv.setSourceNeFields(sourceFieldNew);
            tv.setState(TableVo.DIFF);
            tv.setDiffByField(true);
        }
        if (!targetFieldNew.isEmpty()) {
            tv.setTargetNewFields(targetFieldNew);
            tv.setState(TableVo.DIFF);
            tv.setDiffByField(true);
        }
        // 检查主键
        if (source.getKey() != null) {
            if (source.getKey().size() != target.getKey().size()) {
                tv.setDiffByKey(true);
                tv.setState(TableVo.DIFF);
            } else {
                for (String s : source.getKey()) {
                    boolean has = false;
                    for (String t : target.getKey()) {
                        if (s.equalsIgnoreCase(t)) {
                            has = true;
                            break;
                        }
                    }
                    if (!has) {
                        tv.setDiffByKey(true);
                        tv.setState(TableVo.DIFF);
                        break;
                    }
                }
            }
        } else if (!target.getKey().isEmpty()) {
            tv.setDiffByKey(true);
            tv.setState(TableVo.DIFF);
        }
        // 检查索引
        if (source.getIndexes() != null) {
            for (TableIndex s : source.getIndexes()) {
                if (Strings.isBlank(s.getFields()))
                    continue;
                boolean has = false;
                for (TableIndex t : target.getIndexes()) {
                    has = checkIndex(s.getFields(), t.getFields());
                    if (has) {
                        has = true;
                        break;
                    }
                }
                if (!has) {
                    tv.setDiffByIndex(true);
                    tv.setState(TableVo.DIFF);
                    break;
                }
            }
        } else if (!target.getIndexes().isEmpty()) {
            tv.setDiffByIndex(true);
            tv.setState(TableVo.DIFF);
        }
        // 检查外键
        if (source.getForeignKeys() != null) {
            for (TableFk s : source.getForeignKeys()) {
                if (Strings.isBlank(s.getReferences())
                        || Strings.isBlank(s.getSourceFields())
                        || Strings.isBlank(s.getToFields()))
                    continue;
                boolean has = false;
                for (TableFk t : target.getForeignKeys()) {
                    if (s.getReferences().equalsIgnoreCase(t.getReferences())
                            && s.getSourceFields().equalsIgnoreCase(t.getSourceFields())
                            && s.getToFields().equalsIgnoreCase(t.getToFields())) {
                        has = true;
                        break;
                    }
                }
                if (!has) {
                    tv.setDiffByFk(true);
                    tv.setState(TableVo.DIFF);
                    break;
                }
            }
        } else if (!target.getForeignKeys().isEmpty()) {
            tv.setDiffByFk(true);
            tv.setState(TableVo.DIFF);
        }
        //检查自增
        if (source.isByDb()) {
            if (target.isByDb() == false) {
                tv.setDiffByFk(true);
                tv.setState(TableVo.DIFF);
            }
        } else if (target.isByDb()) {
            tv.setDiffByAutoIncrement(true);
            tv.setState(TableVo.DIFF);
        }

        tv.setTarget(target);
    }

    private boolean checkField(TableField s, TableField t) {
        if (s.getType() == FieldType.VARCHAR || s.getType() == FieldType.CHAR) {
            if (s.getLength() != t.getLength())
                return false;
        } else if (s.getType() == FieldType.NUMERIC) {
            if (t.getType() != FieldType.NUMERIC)
                return false;
            if (s.getLength() > t.getLength()
                    || t.getDecimal() < s.getDecimal())
                return false;
        } else if (s.getType() == FieldType.INT) {
            if (t.getType() == FieldType.LONG
                    || ((t.getType() == FieldType.INT || t.getType() == FieldType.NUMERIC) && t
                    .getLength() >= s.getLength()))
                return true;
        } else if (s.getType() == FieldType.BOOLEAN) {
            if ((t.getType() == FieldType.INT || t.getType() == FieldType.NUMERIC)
                    && t.getLength() == 1)
                return true;
        } else if (s.getType() == FieldType.DATE) {
            if (t.getType() == FieldType.DATETIME)
                return true;
        } else if (s.getType() == FieldType.LONG) {
            if (t.getType() == FieldType.NUMERIC
                    && t.getLength() > s.getLength() && t.getDecimal() == 0)
                return true;
        }
        return s.getType() == t.getType();
    }

    private boolean checkIndex(String source, String target) {
        String[] str1 = source.split(",");
        String[] str2 = target.split(",");
        for (String s : str1) {
            boolean has = false;
            for (String t : str2) {
                if (s.equalsIgnoreCase(t)) {
                    has = true;
                    break;
                }
            }
            if (!has)
                return false;
        }
        return true;
    }
}
